#ifndef _SYM_TABLE_HPP
#define _SYM_TABLE_HPP

#include <string>
#include <map>
#include <vector>
using namespace std;

#include "typechecker.hpp"

class Variable {
private:
    int _id;
    Type* _type;

public:
    Variable(int id, Type* type) : _id(id), _type(type) {}
};

class Node;

class SymbolTable {
private:
    map<string, int>    _name_map;
    map<string, Type*>  _name_type;
    vector<string>      _names;

    map<string, int>    _var_name_map;
    map<string, Type*>  _var_name_type;
    vector<string>      _var_names;

    map<string, int>    _local_map;
    map<string, Type*>  _local_type;
    vector<string>      _local_names;

    map<string, int>    _free_map;
    map<string, Type*>  _free_type;
    vector<string>      _freevars;

    map<string, int>    _cell_map;
    map<string, Type*>  _cell_type;
    vector<string>      _cellvars;

    bool                _is_func;
    Type*               _ret_type;
    int                 _arg_count;

    map<string, int>    _generic_args;
    map<string, Node*>  _subtrees;

    TypeTable*          _type_table;

public:
    SymbolTable*        _parent;
    static SymbolTable*   builtin_symtable;

public:
    SymbolTable(SymbolTable* n = nullptr, bool is_func = false) : 
        _parent(n), _is_func(is_func), _arg_count(0) {
        if (n) {
            _type_table = new TypeTable(n->type_table());
        }
        else {
            _type_table = new TypeTable();
        }
    }

    int size() { return _names.size(); }
    void add_name(const string& name, Type* type);
    int get_name_index(const string& name);
    Type* get_name_type(const string& name);
    void set_name_type(const string& name, Type* type);
    vector<string>& names()     { return _names; }

    void add_local(const string& name, Type* type);
    int get_local_index(const string& name);
    Type* get_local_type(const string& name);
    void set_local_type(const string& name, Type* type);
    vector<string>& local_names()     { return _local_names; }

    int get_var_name_index(const string& name);
    Type* get_var_name_type(const string& name);
    void add_var_name(const string& name, Type* type);
    void set_var_name_type(const string& name, Type* type);
    vector<string>& var_names() { return _var_names; }

    int get_free_var_index(const string& name);
    Type* get_free_var_type(const string& name);
    void add_free_var(const string& name, Type* type);
    vector<string>& freevars() { return _freevars; }

    int get_cell_var_index(const string& name);
    Type* get_cell_var_type(const string& name);
    void add_cell_var(const string& name);
    vector<string>& cellvars() { return _cellvars; }

    bool is_func()              { return _is_func; }
    void set_is_func(bool t)    { _is_func = t; }

    Type* ret_type()            { return _ret_type; }
    void set_ret_type(Type* t)  { _ret_type = t; }

    void set_arg_count(int c)   { _arg_count = c; }
    void load_builtin();

    Node* get_subtree_by_name(string& name);
    void set_subtree(string name, Node* n)  { _subtrees[name] = n; }

    TypeTable* type_table() { return _type_table; }
    void set_type_table(TypeTable* t) { _type_table = t; }

    void clear_instantiation() {
        _type_table->clear_instantiation();
    }

    void set_parent(SymbolTable* p) { _parent = p; }
    SymbolTable* parent() { return _parent; }
};

#endif
